﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "StructureWallHole.h"
#include "DbDataSchema.h"
#include "DbObjectUtils.h"
#include "GbmpBuiltInCategoryUniIdentitiesNew.h"
#include "GbmpBuiltInParameterDefinitionsNew.h"
#include "GbmpCalculatorBase.h"
#include "GcmpBuiltInParameterDefinitions.h"
#include "GmIStructureFactory.h"
#include "IBaseGraphicsElementShapeComponent.h"
#include "ICalculatorCollection.h"
#include "ICopyStrategyReporter.h"
#include "IDocument.h"
#include "IElementOpening.h"
#include "IElementParameters.h"
#include "IElementParentReporter.h"
#include "IElementPosition.h"
#include "IElementStatus.h"
#include "IExternalDataComponent.h"
#include "IFamily.h"
#include "IInstance.h"
#include "IInstanceType.h"
#include "IOpeningGraphicsElementShapeComponent.h"
#include "IParameter.h"
#include "IParameterDefinitionLibrary.h"
#include "IParameterOverride.h"
#include "IStructureInstanceBuildingStoreyData.h"
#include "IStructureWall.h"
#include "LevelUtils.h"
#include "NdbObjectSchematic.h"
#include "StructureBasicWall.h"
#include "StructureInstanceHoleValidator.h"
#include "StructureInstanceLevelUtils.h"
#include "StructureInstanceUtils.h"
#include "StructureVariableSectionWall.h"
#include "StructureWallHoleInput.h"
#include "IGeometryRelationshipComponent.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;
using namespace std;

namespace gcmp
{
    class StructureWallHoleLevelOverride :public NdbObjectSchematic, public IParameterOverride
    {
        DBOBJECT_DATA_BEGIN_AND_QUICK_CAST_FROM(gcmp, StructureWallHoleLevelOverride, gcmp::NdbObjectSchematic, EAECF1FD - A1EB - 4C45 - B11A - FA6EE4D87509, gmstructure, gcmp::IParameterOverride)
        DBOBJECT_DATA_END
    public:
        virtual UniIdentity GetUniIdentity() const override { return PARAMETER_UID(BottomAssociatedLevelBuiltInParameter); }
        virtual bool ShouldOverrideIsModifiable() const override { return false; }
        virtual bool IsModifiable() const override { return true; }
        virtual bool ShouldOverrideIsUserVisible() const override { return false; }
        virtual bool IsUserVisible() const override { return true; }
        virtual bool ShouldOverrideGetName() const override { return true; }
        virtual std::wstring GetName() const override { return L"参照标高"; }
        virtual bool ShouldOverrideGetValidator() const override { return false; }
        virtual const IParameterValidator * GetValidator() const override { return nullptr; }

        virtual IElement* GetOwnerElement() override { return nullptr; }
        virtual const IElement* GetOwnerElement() const override { return nullptr; }
        virtual bool SetOwnerElement(IElement* pElement) override { return true; }

    public: //实现NdbObject接口
        virtual NdbObject* GetTopOwnerObject() const override
        {
            return nullptr;
        }
    };

    DBOBJECT_DATA_DEFINE(StructureWallHoleLevelOverride)
    {
    }

    class StructureWallHoleBottomOffsetOverride :public NdbObjectSchematic, public IParameterOverride
    {
        DBOBJECT_DATA_BEGIN_AND_QUICK_CAST_FROM(gcmp, StructureWallHoleBottomOffsetOverride, gcmp::NdbObjectSchematic, D016443A - EB3B - 4F91 - AE59 - 39F3B83A40DB, gmstructure, gcmp::IParameterOverride)
        DBOBJECT_DATA_END
    public:
        virtual UniIdentity GetUniIdentity() const override { return PARAMETER_UID(BottomOffsetBuiltInParameter); }
        virtual bool ShouldOverrideIsModifiable() const override { return false; }
        virtual bool IsModifiable() const override { return true; }
        virtual bool ShouldOverrideIsUserVisible() const override { return false; }
        virtual bool IsUserVisible() const override { return true; }
        virtual bool ShouldOverrideGetName() const override { return true; }
        virtual std::wstring GetName() const override { return L"标高偏移"; }
        virtual bool ShouldOverrideGetValidator() const override { return false; }
        virtual const IParameterValidator * GetValidator() const override { return nullptr; }

        virtual IElement* GetOwnerElement() override { return nullptr; }
        virtual const IElement* GetOwnerElement() const override { return nullptr; }
        virtual bool SetOwnerElement(IElement* pElement) override { return true; }
    public: //实现NdbObject接口
        virtual NdbObject* GetTopOwnerObject() const override
        {
            return nullptr;
        }
    };

    DBOBJECT_DATA_DEFINE(StructureWallHoleBottomOffsetOverride)
    {
    }

    // 自定义墙洞所属楼层的行为：所属楼层是否可修改
    class StructureWallHoleStoreyOverride :public NdbObjectSchematic, public IParameterOverride
    {
        DBOBJECT_DATA_BEGIN_AND_QUICK_CAST_FROM(gcmp, StructureWallHoleStoreyOverride, gcmp::NdbObjectSchematic, 05B57116 - DF19 - 4860 - 979C - E59472DD5693, gmstructure, gcmp::IParameterOverride)
        DBOBJECT_DATA_END
    public:
        virtual UniIdentity GetUniIdentity() const override { return PARAMETER_UID(BuildingStoreyBuiltInParameter); }
        virtual bool ShouldOverrideIsModifiable() const override { return true; }
        virtual bool IsModifiable() const override { return false; }
        virtual bool ShouldOverrideIsUserVisible() const override { return false; }
        virtual bool IsUserVisible() const override { return true; }
        virtual bool ShouldOverrideGetName() const override { return false; }
        virtual std::wstring GetName() const override { return L""; }
        virtual bool ShouldOverrideGetValidator() const override { return false; }
        virtual const IParameterValidator * GetValidator() const override { return nullptr; }

        virtual IElement* GetOwnerElement() override { return nullptr; }
        virtual const IElement* GetOwnerElement() const override { return nullptr; }
        virtual bool SetOwnerElement(IElement* pElement) override { return true; }

    public: //实现NdbObject接口
        virtual NdbObject* GetTopOwnerObject() const override
        {
            return nullptr;
        }
    };

    DBOBJECT_DATA_DEFINE(StructureWallHoleStoreyOverride)
    {
    }
}

DEFINE_STRUCTURE_INSTANCE_METHODS(StructureWallHole)
DBOBJECT_DATA_DEFINE(StructureWallHole)
{
    m_pOwnerElement = nullptr;
    SetZPositioningType(WallHoleZPositioningType::Center);
    SetHoleType__(WallHoleType::Rect);
    SetConcreteStrengthGrade__(ConcreteStrengthGradeType::CSG_C30);
}

gcmp::StructureWallHole* gcmp::StructureWallHole::Create(const StructureWallHoleInput* pInput)
{
    DBG_WARN_AND_RETURN_UNLESS(pInput, nullptr, L"pInput为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_UNLESS(pInput->Document, nullptr, L"文档为空",L"GDMPLab",L"2024-03-30");

    IDocument* pDoc = pInput->Document;
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    IInstanceType* pInstanceType = quick_cast<IInstanceType>(pDoc->GetElement(pInput->InstanceTypeId));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInstanceType, L"pInstanceType为空",L"GDMPLab",L"2024-03-30");

    IInstance* pInstance = pInstanceType->CreateInstance(L"墙洞实例", pInput->CanBeShared, pInput->CreationOption, pInput->IsCreateGeometryRelationshipComponent);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInstance, L"pInstance为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<StructureWallHoleValidator> pWallHoleValidiator = NEW_AS_OWNER_PTR(StructureWallHoleValidator);
    IBaseGraphicsElementShapeComponent* pBaseShapeComponent = pInstance->GetBaseGraphicsElementShapeComponent();
    DBG_WARN_AND_RETURN_UNLESS(pBaseShapeComponent, nullptr, L"pBaseShapeComponent为空",L"GDMPLab",L"2024-03-30");
    pBaseShapeComponent->SetValidator(TransferOwnership(pWallHoleValidiator));

    //设置主体构件
    IInstance* pHostInstance = quick_cast<IInstance>(pDoc->GetElement(pInput->HostId));
    pInstance->SetHostInstanceId(pInput->HostId);

    //将墙洞注册成外部数据
    OwnerPtr<StructureWallHole> opWallHole = NEW_AS_OWNER_PTR(StructureWallHole);
    StructureWallHole* pWallHole = opWallHole.get();
    pWallHole->SetOwnerElement(pInstance);
    if (IExternalDataComponent * pExternalDataComponent = pInstance->GetExternalDataComponent())
    {
        pExternalDataComponent->RegisterExternalData(opWallHole->GetClassSchema()->GetName(), TransferOwnership(opWallHole));
    }
    if (pHostInstance)
    {
        IElementPosition::CreatePointOnCurve2dOnBottomAssociatedPlanePositionBehavior(pInstance, pInput->AssociatedPlaneId, pInput->Position);
    }
    else
    {
        IElementPosition::CreatePoint2dOnBottomAssociatedPlanePositionBehavior(pInstance, pInput->AssociatedPlaneId, pInput->Position, Vector3d::UnitX, Vector3d::UnitY, pInput->AssociatedPlaneOffset);
    }

    //自定义参数行为
    {
        IElementParameters * pElementParameters = pInstance->GetElementParameters();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElementParameters, L"参数行为为空",L"GDMPLab",L"2024-03-30");

        //Z轴定位
        OwnerPtr<IParameter> paramZPositioningType = pElementParameters->GetParameterByUid(PARAMETER_UID(WallHoleZPositioningTypeBuiltInParameter));
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(IParameter::IsValidParameter(paramZPositioningType), L"paramZPositioningType无效",L"GDMPLab",L"2024-03-30");
        paramZPositioningType->SetValueAsInt((int)pInput->ZPositioningType);
        pElementParameters->SetParameter(paramZPositioningType.get());

        //参数改名
        pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(StructureWallHoleLevelOverride));
        pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(StructureWallHoleBottomOffsetOverride));
        pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(StructureWallHoleStoreyOverride));
    }

    //洞口的所属楼层与主体一致
    if (pHostInstance)
    {
        bool status = StructureInstanceLevelUtils::CreateInstanceStoreyLevel(pDoc, pInstance->GetElementId(), IElementPosition::GetAssociatedPlaneId(pHostInstance));
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(status, L"设置所属楼层失败",L"GDMPLab",L"2024-03-30");
    }

    //可见性设置
    IElementStatus* pStatus = pInstance->GetStatus();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pStatus, L"无法获取状态集",L"GDMPLab",L"2024-03-30");
    pStatus->SetIsVisible(pInput->Visible);
    IGeometryRelationshipComponent* pGeometryRepCpt = pInstance->GetGeometryRelationshipComponent();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pGeometryRepCpt != nullptr, L"无效的指针",L"GDMPLab",L"2024-03-30");
    IOpeningGraphicsElementShapeComponent* pOpeningGRepComponent = pGeometryRepCpt->GetOpeningGraphicsElementShapeComponent();
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pOpeningGRepComponent != nullptr, L"无效的指针",L"GDMPLab",L"2024-03-30");
    pOpeningGRepComponent->SetShouldCalculateIntersectingGraphicsElementShape(true);

    if (pHostInstance)
    {
        //打洞关系建立
        IElementOpening::Create(pDoc, pInstance->GetElementId(), pHostInstance->GetElementId());
    }
    
    return pWallHole;
}

DECLARE_CALCULATOR_CREATOR(WallHoleThicknessParameterCalculator)
DECLARE_CALCULATOR_CREATOR(WallHoleAssociatedLevelCalculator)
void gcmp::StructureWallHole::GetCalculators(ICalculatorCollection * calculators) const
{
    const IElement* pElement = this->GetOwnerElement();
    const IElementParameters* pElementParameters = pElement->GetElementParameters();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pElementParameters, L"参数行为为空",L"GDMPLab",L"2024-03-30");

    ADD_CALCULATOR_OVERRIDE_REGENDATA(WallHoleThicknessParameterCalculator, GetDocument(), pElementParameters->GetDriveParameterRdId());

    ElementId wallHoleId = pElement->GetElementId();
    const IInstance* pWallHoleInstance = quick_cast<const IInstance>(GetDocument()->GetElement(wallHoleId));
    DBG_WARN_AND_RETURN_VOID_UNLESS(pWallHoleInstance, L"pWallHoleInstance为空",L"GDMPLab",L"2024-03-30");
    const IStructureInstanceBuildingStoreyData* pWallHoleStoreyData = IStructureInstanceBuildingStoreyData::Get(pWallHoleInstance);
    if (pWallHoleStoreyData != nullptr)
    {
        RegenDataId wallHoleStoreyDataId = pWallHoleStoreyData->GetBuildingStoreyIdRdId();
        ADD_CALCULATOR(WallHoleAssociatedLevelCalculator, GetDocument(), wallHoleStoreyDataId);
    }
}

void gcmp::StructureWallHole::ReportParents(IElementParentReporter& reporter) const
{
}

void gcmp::StructureWallHole::ReportParameterDefinitions(std::vector<int>* pParamDefIds) const
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pParamDefIds != nullptr, L"pParamDefUids为空",L"GDMPLab",L"2024-03-30");
    pParamDefIds->push_back(PARAMETER_ID(WallHoleZPositioningTypeBuiltInParameter));
}

OwnerPtr<IParameter> gcmp::StructureWallHole::GetNativeParameter(int paramDefId) const
{
    if (paramDefId == PARAMETER_ID(WallHoleZPositioningTypeBuiltInParameter))
    {
        //OwnerPtr<IParameter> param = NEW_AS_OWNER_PTR(Parameter, GetDocument(),
            //ParameterStorageType::Double, false, false, true, PARAMETER_UID(WallHoleZPositioningTypeBuiltInParameter), ParameterProcessType::GeneralInput);
        ParameterAttributes pa = {false, false, true};
        OwnerPtr<IParameter> param = IParameter::CreateParameter(GetDocument(), ParameterStorageType::Double,
            pa, PARAMETER_UID(WallHoleZPositioningTypeBuiltInParameter), ParameterProcessType::GeneralInput);
        param->SetValueAsInt((int32_t)GetZPositioningType__());
        return TransferOwnership(param);
    }

    return nullptr;
}

bool gcmp::StructureWallHole::SetNativeParameter(const IParameter *param, std::wstring* errorMsg)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(param, L"param is null.",L"GDMPLab",L"2024-03-30");

    if (param->GetParameterDefinitionId() == PARAMETER_ID(WallHoleZPositioningTypeBuiltInParameter))
    {
        SetZPositioningType__((WallHoleZPositioningType)param->GetValueAsInt());
        return true;
    }
    return false;
}

bool gcmp::StructureWallHole::IsParameterModifiable(int paramDefId) const
{
    //TODO：如果是圆形洞口，则定位方式不可修改
    return true;
}

void gcmp::StructureWallHole::Report(const gcmp::ICopyContext & copyContext, gcmp::ICopyStrategyReporter& reporter) const
{
    reporter.ReportToCopy(GetOwnerElementId());
}

gcmp::NdbObject* gcmp::StructureWallHole::GetTopOwnerObject() const
{
    if (!m_pOwnerElement)
        return nullptr;
    return quick_cast<NdbObject>(m_pOwnerElement);
}

IDocument * gcmp::StructureWallHole::GetDocument() const
{
    if (!m_pOwnerElement)
    {
        return nullptr;
    }
    return m_pOwnerElement->GetDocument();
}

ElementId gcmp::StructureWallHole::GetOwnerElementId() const
{
    if (!m_pOwnerElement)
    {
        return ElementId::InvalidID;
    }
    return m_pOwnerElement->GetElementId();
}

IStructureWall* gcmp::StructureWallHole::GetHost() const
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pOwnerElement, L"m_pOwnerElement为空",L"GDMPLab",L"2024-03-30");

    ElementId wallId = m_pOwnerElement->GetHostElementId();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(wallId.IsValid(), L"wallId无效",L"GDMPLab",L"2024-03-30");

    IElement* pElement = GetDocument()->GetElement(wallId);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElement, L"pElement为空",L"GDMPLab",L"2024-03-30");

    IInstance* pWallInstance = quick_cast<IInstance>(pElement);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pWallInstance, L"pWallInstance为空",L"GDMPLab",L"2024-03-30");

    IStructureWall* pStructureWall = nullptr;
    IExternalData* pData = nullptr;
    if (IExternalDataComponent * pExternalDataComponent = pWallInstance->GetExternalDataComponent())
    {
        if (pData = pExternalDataComponent->FindExternalData(StructureBasicWall::GetStaticClassSchema()->GetName()))
        {
            pStructureWall = dynamic_cast<IStructureWall*>(pData);
        }
        else if (pData = pExternalDataComponent->FindExternalData(StructureVariableSectionWall::GetStaticClassSchema()->GetName()))
        {
            pStructureWall = dynamic_cast<IStructureWall*>(pData);
        }
        else
        {
            DBG_WARN(L"不支持的墙类型",L"GDMPLab",L"2024-03-30");
        }
    }
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pStructureWall, L"pStructureWall为空",L"GDMPLab",L"2024-03-30");

    return pStructureWall;
}


bool StructureWallHole::SetOwnerElement(IElement* pOwnerElement)
{
    m_pOwnerElement = pOwnerElement;
    return true;
}
